use super::config;
use anyhow::anyhow;
use sea_orm::{ConnectOptions, ConnectionTrait, DatabaseBackend, DatabaseConnection, Statement};
use std::cmp::{max, min};
use std::time::Duration;
use tokio::sync::OnceCell;

static DB: OnceCell<DatabaseConnection> = OnceCell::const_new();

/// 初始化数据库链接
///
/// # 返回
/// - `anyhow::Result<DatabaseConnection>`
/// - `DatabaseConnection` - 数据库链接
pub async fn initial() -> anyhow::Result<DatabaseConnection> {
    let db_config = config::get().database()?;
    let mut options = ConnectOptions::new(
        format!("postgres://{}:{}@{}:{}/{}",
            db_config.user(), db_config.password(), db_config.host(), db_config.port(), db_config.database()
        )
    );

    let cpus = num_cpus::get() as u32;
    options.max_connections(max(cpus * 2, 10))
        .min_connections(min(cpus * 4, 20))
        // 链接超时时间
        .connect_timeout(Duration::from_secs(10))
        // 链接数据读取时间
        .acquire_timeout(Duration::from_secs(30))
        // 最小链接数不够时链接空闲过期时间
        .idle_timeout(Duration::from_secs(300))
        // 链接最大存活时间
        .max_lifetime(Duration::from_secs(300))
        .sqlx_logging(false)
        .set_schema_search_path(db_config.schema());

    let db = sea_orm::Database::connect(options).await?;
    db.ping().await?;

    tracing::info!("数据库初始化成功");

    log_database_version(&db).await?;

    Ok(db)
}

/// 查询当前数据库版本信息
async fn log_database_version(db: &DatabaseConnection) -> anyhow::Result<()> {
    let version = db.query_one(
        Statement::from_string(
            DatabaseBackend::Postgres,
            String::from("SELECT version()"),
        )
    ).await?.ok_or_else(|| anyhow!("数据库链接失败"))?;

    tracing::info!("数据库版本信息 => {}", version.try_get_by_index::<String>(0)?);
    Ok(())
}

pub async fn initialize_static() -> &'static DatabaseConnection {
    DB.get_or_init(|| async {
        initial().await.unwrap()
    }).await
}